/* fflas/fflas_ftrmm.inl
 * Copyright (C) 2010 LinBox
 *
 * Written by Brice Boyer <Brice.Boyer@imag.fr>
 *
 * See COPYING for license information.
 */

#include <iomanip>
#include "linbox/linbox-config.h"

#include "linbox/fflas/fflas.h"
#include "linbox/field/modular-double.h"
#include "linbox/field/modular-balanced-double.h"
#include "linbox/util/timer.h"

using namespace LinBox ;

#define _LB_REPETS 5

/** \brief test
 * <i>toto</i> est grand.
 * <b>toto</b> est grand.
 * @ingroup benchmark
 */

template<class Field, bool _add>
int bench_add(const Field & F,
	      size_t M, size_t N,
	      typename Field::Element *A,
	      typename Field::Element *B,
	      typename Field::Element *C)
{
	Timer tim; tim.clear() ; tim.start();
	for (size_t i = 0 ; i < _LB_REPETS ; ++i)
		if (_add)
			FFLAS::faddm<Field>(F,FFLAS::FflasNoTrans,M,N,A,N,B,N);
		else
			FFLAS::fsubm<Field>(F,FFLAS::FflasNoTrans,M,N,A,N,B,N);
	tim.stop();
	std::cout << "A"<<((_add)?('+'):('-'))<<"=B\t:" << (M*N)/tim.usertime()/1e6 << "Mflops" << std::endl;

	tim.clear() ; tim.start();
	for (size_t i = 0 ; i < _LB_REPETS ; ++i)
		if (_add)
			FFLAS::faddm<Field>(F,FFLAS::FflasTrans,M,N,A,M,B,N);
		else
			FFLAS::fsubm<Field>(F,FFLAS::FflasTrans,M,N,A,M,B,N);
	tim.stop();
	std::cout << "A"<<((_add)?('+'):('-'))<<"=tB\t:" << (M*N)/tim.usertime()/1e6 << "Mflops" << std::endl;

	tim.clear() ; tim.start();
	for (size_t i = 0 ; i < _LB_REPETS ; ++i)
		if (_add)
			FFLAS::faddm<Field>(F,FFLAS::FflasNoTrans,FFLAS::FflasNoTrans,M,N,A,N,B,N,C,N);
		else
			FFLAS::fsubm<Field>(F,FFLAS::FflasNoTrans,FFLAS::FflasNoTrans,M,N,A,N,B,N,C,N);
	tim.stop();
	std::cout << "A=B"<<((_add)?('+'):('-'))<<"C\t:" << (M*N)/tim.usertime()/1e6 << "Mflops" << std::endl;

	tim.clear() ; tim.start();
	for (size_t i = 0 ; i < _LB_REPETS ; ++i)
		if (_add)
			FFLAS::faddm<Field>(F,FFLAS::FflasTrans,FFLAS::FflasNoTrans,M,N,A,M,B,N,C,N);
		else
			FFLAS::fsubm<Field>(F,FFLAS::FflasTrans,FFLAS::FflasNoTrans,M,N,A,M,B,N,C,N);
	tim.stop();
	std::cout << "A=tB"<<((_add)?('+'):('-'))<<"C\t:" << (M*N)/tim.usertime()/1e6 << "Mflops" << std::endl;

	tim.clear() ; tim.start();
	for (size_t i = 0 ; i < _LB_REPETS ; ++i)
		if (_add)
			FFLAS::faddm<Field>(F,FFLAS::FflasNoTrans,FFLAS::FflasTrans,M,N,A,N,B,M,C,N);
		else
			FFLAS::fsubm<Field>(F,FFLAS::FflasNoTrans,FFLAS::FflasTrans,M,N,A,N,B,M,C,N);
	tim.stop();
	std::cout << "A=B"<<((_add)?('+'):('-'))<<"tC\t:" << (M*N)/tim.usertime()/1e6 << "Mflops" << std::endl;

	tim.clear() ; tim.start();
	for (size_t i = 0 ; i < _LB_REPETS ; ++i)
		if (_add)
			FFLAS::faddm<Field>(F,FFLAS::FflasTrans,FFLAS::FflasTrans,M,N,A,M,B,M,C,N);
		else
			FFLAS::fsubm<Field>(F,FFLAS::FflasTrans,FFLAS::FflasTrans,M,N,A,M,B,M,C,N);
	tim.stop();
	std::cout << "A=tB"<<((_add)?('+'):('-'))<<"tC\t:" << (M*N)/tim.usertime()/1e6 << "Mflops" << std::endl;

	return 0;

}

int main(int argc, char** argv)
{
	if (argc != 3) {
		std::cout << "size p" << std::endl;
		return -1;
	}
	size_t max_sz = atoi(argv[1]);
	double card   = atoi(argv[2]);
	std::cout << "benchmarking add on matrices around " << max_sz << " on Z/" << card ;
	srand(time(NULL));
	size_t M    = max_sz/2 + random()%(max_sz/2) ;
	size_t N    = max_sz/2 + random()%(max_sz/2) ;
	std::cout << " (" << M << 'x' << N << ')' << std::endl;

	std::cout << "double" << std::endl;
	{
		typedef Modular<double> Field;
		Field F(card);
		typedef Field::Element Element ;

		Element * A = new Element[M*N] ;
		Element * B = new Element[M*N] ;
		Element * C = new Element[M*N] ;

		typedef Field::RandIter RandIter;
		RandIter G(F);

		for (size_t i = 0 ; i < M ;++i) for (size_t j = 0 ; j < N ;++j) G.random(*(A+i*N+j));
		for (size_t i = 0 ; i < M ;++i) for (size_t j = 0 ; j < N ;++j) G.random(*(B+i*N+j));
		for (size_t i = 0 ; i < M ;++i) for (size_t j = 0 ; j < N ;++j) G.random(*(C+i*N+j));


		bench_add<Field,true>(F,M,N,A,B,C);
		bench_add<Field,false>(F,M,N,A,B,C);

		delete[] A;
		delete[] B;
	}
	std::cout << "float" << std::endl;
	{
		typedef Modular<float> Field;
		Field F((float)card);
		typedef Field::Element Element ;

		Element * A = new Element[M*N] ;
		Element * B = new Element[M*N] ;
		Element * C = new Element[M*N] ;

		typedef Field::RandIter RandIter;
		RandIter G(F);

		for (size_t i = 0 ; i < M ;++i) for (size_t j = 0 ; j < N ;++j) G.random(*(A+i*N+j));
		for (size_t i = 0 ; i < M ;++i) for (size_t j = 0 ; j < N ;++j) G.random(*(B+i*N+j));
		for (size_t i = 0 ; i < M ;++i) for (size_t j = 0 ; j < N ;++j) G.random(*(C+i*N+j));


		bench_add<Field,true>(F,M,N,A,B,C);
		bench_add<Field,false>(F,M,N,A,B,C);

		delete[] A;
		delete[] B;
	}
	std::cout << "int" << std::endl;
	{
		typedef Modular<int32_t_t> Field;
		Field F(card);
		typedef Field::Element Element ;

		Element * A = new Element[M*N] ;
		Element * B = new Element[M*N] ;
		Element * C = new Element[M*N] ;

		typedef Field::RandIter RandIter;
		RandIter G(F);

		for (size_t i = 0 ; i < M ;++i) for (size_t j = 0 ; j < N ;++j) G.random(*(A+i*N+j));
		for (size_t i = 0 ; i < M ;++i) for (size_t j = 0 ; j < N ;++j) G.random(*(B+i*N+j));
		for (size_t i = 0 ; i < M ;++i) for (size_t j = 0 ; j < N ;++j) G.random(*(C+i*N+j));


		bench_add<Field,true>(F,M,N,A,B,C);
		bench_add<Field,false>(F,M,N,A,B,C);

		delete[] A;
		delete[] B;
	}

	return 0;
}


/* -*- mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
// vim:sts=8:sw=8:ts=8:noet:sr:cino=>s,f0,{0,g0,(0,\:0,t0,+0,=s:syntax=cpp.doxygen:foldmethod=syntax
